﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Threading;

namespace Isaac_dm
{
    public partial class IsaacDmManager : Form
    {
        private bool WillDeinit = false;
        private Socket socket = null;
        private int port = 6666;
        private Action<string> log = null, importLog = null;
        
        private LinkedList<Tuple<Socket, StreamReader, StreamWriter, NetworkStream>> gameSockets = new LinkedList<Tuple<Socket, StreamReader, StreamWriter, NetworkStream>>();
        public IsaacDmManager(Action<string> logger = null, Action<string> importLogger = null)
        {
            InitializeComponent();
            this.log = logger;
            this.importLog = importLogger;
            if(this.log == null)
            {
                this.log = (string s) => { };
            }
            if(this.importLog == null)
            {
                this.importLog = this.log;
            }
            ItemIdsTextBox_TextChanged(null, null);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var dialog = new OpenFileDialog();
            dialog.FileName = "isaac-ng.exe";
            if(dialog.ShowDialog() == DialogResult.OK)
            {
                IsaacPathBox.Text = dialog.FileName;
            }
        }

        private void StartGameButton_Click(object sender, EventArgs e)
        {
            bool started = false;
            try
            {
                started = null != Process.Start(new ProcessStartInfo(IsaacPathBox.Text, "--luadebug") {
                    WorkingDirectory = IsaacPathBox.Text + "\\..",
                    UseShellExecute = false
                });
            }
            catch (Exception)
            {

            }
            if (!started)
            {
                MessageBox.Show("由于某些原因，未能启动游戏。您可以在自行启动游戏。");
            }
        }
        public void ReceivedDamaku(BilibiliDM_PluginFramework.DanmakuModel danmaku)
        {
            // todo Thread!!!
            if(danmaku.MsgType == BilibiliDM_PluginFramework.MsgTypeEnum.Comment)
            {
                var text = danmaku.CommentText;
                var user = danmaku.UserName;
                var uid = danmaku.UserID;
                var paizi_info = danmaku.RawDataJToken["info"][3];

                bool hasPaizi = paizi_info.Count() > 2;
                string paizi_name = "";
                int paizi_level = -1;
                if (hasPaizi)
                {
                    paizi_name = ((string)paizi_info[1]);
                    paizi_level = ((int)paizi_info[0]);
                }

                // filter valid user
                if (useUserFilter)
                {
                    lock (userFilter)
                    {
                        if (userFilter.Contains(user) == false)
                            return;
                    }
                }

                AcceptDanmuString(text, user);
            }
        }
        private void ListenButton_Click(object sender, EventArgs e)
        {
            if(socket == null)
            {
                try
                {
                    socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                    socket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), port));
                    socket.Listen(10);
                }catch(SocketException err)
                {
                    MessageBox.Show(err.ToString(),"错误，无法建立与游戏的通讯");
                    return;
                }
                StartWorker();
                log("开始监听，请打开游戏");
                ListenButton.Text = "正在监听";
                ListenButton.Enabled = false;
            }
            else
            {
                log("重复监听");
            }
        }

        private void StartWorker()
        {
            new Thread(()=> {
                while (!WillDeinit)
                {
                    try
                    {
                        Socket gamesocket = socket.Accept();
                        gamesocket.ReceiveTimeout = 1000;
                        NetworkStream ns = new NetworkStream(gamesocket);
                        StreamWriter sw = new StreamWriter(ns);
                        StreamReader sr = new StreamReader(ns);
                        sw.WriteLine("Isaac");
                        sw.Flush();
                        ns.Flush();
                        
                        if ("Isaac" == sr.ReadLine())
                        {
                            lock (gameSockets)
                            {

                                gameSockets.AddLast(Tuple.Create(gamesocket, sr, sw, ns));
                            }
                            log("已检测到游戏连入");
                        }
                        else
                        {
                            sr.Dispose();
                            sw.Dispose();
                            ns.Dispose();
                            gamesocket.Close();
                            log("有人连入，但不是游戏");
                        }
                    }
                    catch (Exception) { }
                }
            }).Start();
        }

        private void SendToGame(string text)
        {
            var closedSockets = new LinkedList<Tuple<Socket, StreamReader, StreamWriter, NetworkStream>>();

            lock (gameSockets)
            {
                foreach(var socket in gameSockets)
                {
                    try
                    {
                        socket.Item3.WriteLine(text);
                        socket.Item3.Flush();
                        socket.Item4.Flush();
                    }
                    catch (SocketException)
                    {
                        closedSockets.AddLast(socket);
                    }
                }
            }
            if (closedSockets.Count > 0)
                log("有" + closedSockets.Count + "个游戏的连接已经丢失（游戏已退出或重启）");
            foreach(var socket in closedSockets)
            {
                socket.Item4.Dispose();
                socket.Item3.Dispose();
                socket.Item2.Dispose();
                socket.Item1.Dispose();
                gameSockets.Remove(socket);
            }
        }

        public void DeInit()
        {
            WillDeinit = true;
            if(socket != null)
                socket.Close();
            Close();
        }

        private void IsaacDmManager_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (WillDeinit)
            {
                return;
            }
            e.Cancel = true;
            this.Hide();
        }
        private HashSet<string> userFilter = new HashSet<string>();
        private bool useUserFilter = false;
        private void UserFilterTextBox_TextChanged(object sender, EventArgs e)
        {
            lock (userFilter)
            {
                userFilter.Clear();
                foreach(var line in UserFilterTextBox.Lines)
                {
                    userFilter.Add(line);
                }
            }
        }
        private HashSet<int> itemIdFilter = new HashSet<int>();
        private void ItemIdsTextBox_TextChanged(object sender, EventArgs e)
        {
            lock (itemIdFilter)
            {
                itemIdFilter.Clear();
                foreach(var line in ItemIdsTextBox.Lines)
                {
                    if(int.TryParse(line, out int r))
                    {
                        itemIdFilter.Add(r);
                    }
                }
            }
        }

        //item-id, item count
        private Dictionary<int, int> GetItemIdCountList()
        {
            var ret = new Dictionary<int, int>();
            lock (recordedUserItems)
            {
                foreach (var item in recordedUserItems)
                {
                    if (ret.ContainsKey(item.Value))
                    {
                        ret[item.Value]++;
                    }
                    else
                    {
                        ret.Add(item.Value, 1);
                    }
                }
            }
            return ret;
        }

        private void FlushUserItemsUI()
        {
            var str = new StringBuilder();
            lock (recordedUserItems)
            {
                foreach (var item in recordedUserItems)
                {
                    str.Append(item.Key).Append("：点了道具").Append(item.Value).AppendLine();
                }
            }
            string UserItemText = str.ToString();
            BeginInvoke(new Action(() => {
                UserItemRecordTextBox.Text = UserItemText;
            }));

            var sorted_list = GetItemIdCountList().ToList();
            sorted_list.Sort((a, b) => { return a.Value - b.Value; });
            str.Clear();
            foreach(var item in sorted_list)
            {
                str.Append("道具").Append(item.Key).Append(" 权重：").Append(item.Value).AppendLine();
            }
            string ItemSelectListText = str.ToString();
            BeginInvoke(new Action(() => { 
                ItemSelectListTextBox.Text = ItemSelectListText; 
            }));
        }
        //username-itemid
        private Dictionary<string, int> recordedUserItems = new Dictionary<string, int>();
        private void AcceptDanmuString(string danmu, string username)
        {
            //maybe not UI thread!
            if(useItemDanmu && danmu.StartsWith("道具") && int.TryParse(danmu.Substring(2), out int itemId))
            {
                //filter item id
                lock (itemIdFilter)
                {
                    if (itemIdFilter.Contains(itemId))
                    {
                        lock (recordedUserItems)
                        {
                            if (recordedUserItems.ContainsKey(username))
                            {
                                recordedUserItems[username] = itemId;
                            }
                            else
                            {
                                recordedUserItems.Add(username, itemId);
                            }
                            FlushUserItemsUI();
                        }
                    }
                }
            }
        }

        private void DanmuSimulateAcceptButton_Click(object sender, EventArgs e)
        {
            AcceptDanmuString(DanmuSimulateTextBox.Text,"虚假的用户");
            DanmuSimulateTextBox.Text = "";
        }

        private void UserFilterCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            useUserFilter = UserFilterCheckBox.Checked;
        }

        private void UserItemRecordClearButton_Click(object sender, EventArgs e)
        {
            lock (recordedUserItems)
            {
                recordedUserItems.Clear();
            }
        }
        bool useItemDanmu = false;
        private void UseItemDanmuCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            useItemDanmu = UseItemDanmuCheckBox.Checked;
        }

        private void SendCmdButton_Click(object sender, EventArgs e)
        {
            SendToGame(cmdTextBox.Text);
            importLog("已发送指令 " + cmdTextBox.Text);
            cmdTextBox.Text = "";
        }
    }
}
